/* -*- C++ -*- */
/*===========================================================
 
 .---=||||[ B L O O D Y ]|||||=====================----------....
 |                                                              |
 |    THE  M O S T   B L O O D Y  GAME EVER!                    |
 |                                                              |
 |    bloody harvest is coming... soon,                         |
 |                                              heil Satan!     |
 !                                                              !
 .                                                              ,
  ~~~~-------------================================|||||||||==-~




 Name-Space: {core, logic, editor}
 Module: {draw, coll, scgr, ...}
 Class: <name of class>
 Last modification:  <author>, <time>

 TODO:
 <-------- todo #1 ---------->
 <-------- todo #2 ---------->
 <-------- todo #3 ---------->
        ............
 <-------- todo #n ---------->

 FIX:
 <-------- fix #1 ----------->
 <-------- fix #1 ----------->
         ..........
 <-------- fix #n ----------->




 
 
 ============================================================*/
#ifndef __AABB_H_
#define __AABB_H_

#include "common.h"
#include "core/vmath.h"

namespace core
{
	namespace coll
	{
	

		/**
		 * 2D Axis align bounding box. This class is used for fast colision detection in 2D. 
		 * Actualy AABB can colide with: Point, another AABB, and ray.
		 */
		template <class T>
		class AABB2
		{
		public:
			/// Minimal box
			Vector2<T> minBox;
			/// Maixmal box
			Vector2<T> maxBox;
		
			///Constructor
			AABB2(){}
		
			/// Copy constructor
			AABB2(const AABB2<T>& b):minBox(b.minBox),maxBox(b.maxBox){}
		
			/// Constructor
			AABB2(Vector2<T> n, Vector2<T> x):minBox(n),maxBox(x){}
		
			/// Is point inside AABB
			bool isIn(Vector2<T>& v)
			{
				if (minBox.x <= v.x && maxBox.x >= v.x &&
					minBox.y <= v.y && maxBox.y >= v.y)
				{
					return true;
				}
				return false;
			}
		
			/// Assign operator
			AABB2<T>& operator=(AABB2<T>& v)
			{
				minBox = v.minBox;
				maxBox = v.maxBox;
				return *this;
			}
		
			/// Is bounding box present inside this AABB
			bool isIn(AABB2<T>& box)
			{
				Vector2<T> c[4] = { 
					Vector2<T>(box.minBox), Vector2<T>(box.maxBox.x, box.minBox.y), 
					Vector2<T>(box.maxBox), Vector2<T>(box.minBox.x, box.maxBox.y)
				};
				for (int i = 0; i < 4; i++)
				{
					if (isIn(c[i]))
					{
						return true;
					}
				}
				Vector2<T> c2[4] = { 
					Vector2<T>(minBox), Vector2<T>(maxBox.x, minBox.y), 
					Vector2<T>(maxBox), Vector2<T>(minBox.x, maxBox.y)
				};
		
				for (int i = 0; i < 4; i++)
				{
					if (box.isIn(c2[i]))
					{
						return true;
					}
				}
		
				if (box.minBox.x <= minBox.x && box.maxBox.x >= maxBox.x 
					&& box.minBox.y >= minBox.y && box.maxBox.y <= maxBox.y)
				{
					return true;
				}
		
				if (box.minBox.y <= minBox.y && box.maxBox.y >= maxBox.y 
					&& box.minBox.x >= minBox.x && box.maxBox.x <= maxBox.x)
				{
					return true;
				}
				return false;
			}
		
			/// Update value of bounding box to unite vector v.
			void update(Vector2<T> v)
			{
				if (v.x > maxBox.x) maxBox.x = v.x;
				if (v.y > maxBox.y) maxBox.y = v.y;
				//if (v.z > maxBox.z) maxBox.z = v.z;
		
				if (v.x < minBox.x) minBox.x = v.x;
				if (v.y < minBox.y) minBox.y = v.y;
				//if (v.z < minBox.z) minBox.z = v.z;
			}
		
			/**
			 * Reset bounding box to value. This is used to begin computation of bounding box. Vector v is the intial value. 
			 * To continue computing bounding box, pass every vertex (expect the intial) to update method.
			 */
			void reset(Vector2<T> v)
			{
				minBox = maxBox = v;
			}
		
			/**
			 * Returns center of AABB. The center is computed as min + (max-min) * 0.5 .
			 */
			Vector2<T> center()
			{
				return (minBox + maxBox) * 0.5;
			}
		
			/**
			 * Validate bounding box. Test if bounding box sides are bigger then epsilon.
			 */
			bool validate(T epsilon = 1)
			{
				return ((maxBox.x - minBox.x) > epsilon) && ((maxBox.y - minBox.y) > epsilon) /*&& ((maxBox.z - minBox.z) > epsilon)*/;
			}
		
			/**
			 * Ray bounding box intersection. Returns colision distance from bounding box. This number can be used to determine the nearest
			 * colider from ray origin.
			 * Note: Distance value is OK only for ray outside of AABB.
			 * @return -1 if ray miss AABB, distance value for ray hit AABB. Note Distance value is OK for rays outside of AABB.
			 */
			T rayColide(Vector2<T> a, Vector2<T> q)
			{
				// near and far intersection points
				T tn,tf;
				tn = 0; tf = 2e31;
				// maximal and minimal point of AABB
				Vector2<T> x,n;
				n = minBox;
				x = maxBox;
		
				// check distance from origin of ray (a)
				Vector2<T> d1,d2;
				d1 = a - minBox;
				d2 = a - maxBox;
				// repair maximal and minmal points of AABB
				if (d1.lengthSq() > d2.lengthSq())
				{
					x = minBox;
					n = maxBox;
				}
				// check intersection with planes
				for (int i = 0; i < 2; i++)
				{
					T tni = (n[i] - a[i])/q[i];
					T tfi = (x[i] - a[i])/q[i];
					tn = (tni > tn) ? tni : tn;
					tf = (tfi < tf) ? tfi : tf;
					if (tf < tn)
					{
						return -1;
					}
				}
				// tn is colision distance value (the ray is not in AABB!!!)
				return tn;
			}
		
			/**
			 * Test if circle with origin in s and radius r colide
			 */
			bool circleColide(Vector2<T> s, T r)
			{
				Vector2<T> w(r,r);
				AABB2<T> cbb(minBox - w * 0.5, maxBox + w * 0.5);
				return cbb.isIn(s);
			}

			/*
			/// Draws AABB 
			void draw()
			{
				Vector3<T> c[4] = {Vector3<T>(minBox.x, 0, minBox.y), Vector3<T>(maxBox.x,0, minBox.y), Vector3<T>(maxBox.x, 0, maxBox.y), Vector3<T>(minBox.x, 0, maxBox.y)};
		
				glBegin(GL_LINES);
				for (int i = 0; i < 4; i++)
				{
					int idx1,idx2;
					idx1 = i;
					idx2 = (i+1) % 4;
					glVertex3fv(c[idx1]);
					glVertex3fv(c[idx2]);
				}
				glEnd();
			}
			*/
		
		};

		typedef AABB2<f32> AABB2f;

		/**
		 * Three dimensional Axes Aligned Bounding Box template class.
		 */
		template <class T>
		class AABB3
		{
		public:
			/**
			 * Top left corner of AABB.
			 */
			Vector3<T> minBox;

			/**
			 * Bottom right corner of AABB.
			 */
			Vector3<T> maxBox;

			/**
			 * Creates empty AABB with center in (0,0,0)
			 */
			AABB3(){}

			/**
			 * Creates copy of AABB3 object.
			 *
			 * @param orig Object to be copied.
			 */
			AABB3(const AABB3<T>& orig)
				: minBox(orig.minBox), maxBox(orig.maxBox)
			{}

			/**
			 * Creates new AABB3 object based of corner points.
			 *
			 * @param n Minimal corner (top left)
			 * @param x Maximal corner (bottom right)
			 */
			AABB3(const Vector3<T>& n, const Vector3<T>& x)
				: minBox(n), maxBox(x)
			{}

			/**
			 * Assign operator
			 *
			 * @param rhs Right hand side argument of operator
			 * @return itself
			 */
			AABB3<T>& operator=(const AABB3<T>& rhs)
			{
				minBox = rhs.minBox;
				maxBox = rhs.maxBox;

				return *this;
			}

			/**
			 * Add operator
			 * @param rhs Right hand side operator.
			 * @return Result of addition.
			 */
			AABB3<T> operator+(const Vector3<T>& rhs){
				return AABB3<T>(minBox + rhs, maxBox + rhs);
			}

			/**
			 * Incerement operator
			 * @param rhs Right hand side operator.
			 * @return Result of addition.
			 */
			AABB3<T>& operator+=(const Vector3<T>& rhs){
				minBox += rhs;
				maxBox += rhs;
				return *this;
			}

			/**
			 * Substraction operator
			 *
			 * @param rhs Right hand side operator.
			 * @return Result of substraction.
			 */
			AABB3<T> operator-(const Vector3<T>& rhs){
				return AABB3<T>(minBox - rhs, maxBox - rhs);
			}

			/**
			 * Decerement operator
			 *
			 * @param rhs Right hand side operator.
			 * @return Result of substraction.
			 */
			AABB3<T>& operator-=(const Vector3<T>& rhs){
				minBox -= rhs;
				maxBox -= rhs;
				return *this;
			}


			/**
			 * Gets center of AABB
			 *
			 * @return center of AABB
			 */
			Vector3<T> getCenter() const
			{
				return (minBox + maxBox) * 0.5;
			}

			/**
			 * Gets AABB extent
			 *
			 * @return extent of AABB
			 */
			Vector3<T> getExtent() const
			{
				return (maxBox - minBox);
			}

			/**
			 * Repairs AABB, so minBox is real top-left corner 
			 * and maxBox is real bottom-right corner
			 */
			void repair()
			{
				if (minBox.x > maxBox.x)
				{
					T tmp = minBox.x;
					minBox.x = maxBox.x;
					maxBox.x = tmp;
				}

				if (minBox.y > maxBox.y)
				{
					T tmp = minBox.y;
					minBox.y = maxBox.y;
					maxBox.y = tmp;
				}

				if (minBox.z > maxBox.z)
				{
					T tmp = minBox.z;
					minBox.z = maxBox.z;
					maxBox.z = tmp;
				}
			}

			/**
			 * Resets AABB to zero size with ceneter in origin.
			 */
			void reset()
			{
				minBox = maxBox = Vector3f();
			}


			/**
			 * Adjust current AABB so point p fits in it.
			 *
			 * @param p Point to adjust AABB.
			 */
			void addPoint(const Vector3<T>& p)
			{
				if (minBox.x > p.x)
				{
					minBox.x = p.x;
				}

				if (minBox.y > p.y)
				{
					minBox.y = p.y;
				}

				if (minBox.z > p.z)
				{
					minBox.z = p.z;
				}



				if (maxBox.x < p.x)
				{
					maxBox.x = p.x;
				}

				if (maxBox.y < p.y)
				{
					maxBox.y = p.y;
				}

				if (maxBox.z < p.z)
				{
					maxBox.z = p.z;
				}
			}


			/**
			 * Adjust current AABB so box b fits in it.
			 *
			 * @param b Box to adjust AABB.
			 */
			void addBox(const AABB3<T>& b)
			{
				addPoint(b.minBox);
				addPoint(b.maxBox);
			}

			/**
			 * Test if point p is inside of current AABB
			 *
			 * @param p Point to be tested.
			 * @return true if p is inside AABB, otherwise false.
			 */
			bool isIn(const Vector3<T>& p) const
			{
				return (p.x <= minBox.x && p.x >= maxBox.x) 
					&& (p.y <= minBox.y && p.y >= maxBox.y) 
					&& (p.z <= minBox.z && p.z >= maxBox.z);
			}


			/**
			 * Test if Box b intersects current AABB
			 *
			 * @param b Box to be tested.
			 * @return true if b intersects current AABB, otherwise false.
			 */
			bool isIn(const AABB3<T>& other) const
			{
				return le(minBox,  other.maxBox) && ge(maxBox, other.minBox);
			}


			/**
			 * Test if circle with origin in center and radius intersects
			 * with current box.
			 * 
			 * @param center Center of sphere
			 * @param radius Radius of spehere
			 * @return True if intersection happends, otherwise false.
			 */
			bool sphereTest(const Vector3<T> center, float radius){
				AABB3<T> tmp(minBox - radius, maxBox + radius);
				return tmp.isIn(center);
			}





		private:
			/**
			 * Less or equal operator on Vector3<T>
			 *
			 * @param lhs Left hand side argument of operator.
			 * @param rhs Right hand side argument of operator.
			 * @return True if lhs.x <= rhs.x and lhs.y <= rhs.y
			 * and lhs.z <= rhs.z, otherwise false.
			 */
			static bool le(const Vector3<T>& lhs, const Vector3<T>& rhs)
			{
				return lhs.x <= rhs.x && lhs.y <= rhs.y && lhs.z <= rhs.z;
			}

			/**
			 * Greater or equal operator on Vector3<T>
			 *
			 * @param lhs Left hand side argument of operator.
			 * @param rhs Right hand side argument of operator.
			 * @return True if lhs.x >= rhs.x and lhs.y >= rhs.y
			 * and lhs.z >= rhs.z, otherwise false.
			 */
			static bool ge(const Vector3<T>& lhs, const Vector3<T>& rhs)
			{
				return lhs.x >= rhs.x && lhs.y >= rhs.y && lhs.z >= rhs.z;
			}

			
		};

		typedef AABB3<f32> AABB3f;

	}
}
#endif // __AABB_H_

	 
